USB通信 (VC#)                          

  



■ PIC18 CDCクラスMicrochip社デモソフト 

  PICとUSB通信を行なう場合、マイクロチップ社から提供(公開)されているいるUSBフレームワークのCDCクラスを使うとPC側のソフトは
 RS232Cの送受信のようにあつかうことができます。このサンプルソフトがマイクロチップ社のHPに公開されています。以下のプログラムは 
 マイクロチップ社のMCHPFSUSB Framework 2.3をダウンロードして解凍後PCにできる …\Microchip Solutions\
 USB Device - CDC - Basic Demo\PC Software ExampleVCsharp 2005フォルダに収納されているC#のサンプルプログラムです。この
 ソフトは マイクロチップ  DemoBoard(PICDEM FS USB DM163025)($59.99ドル)用デモソフトのPC側ソフトです。 コメント行は翻訳、
 、削除、追記等をしていますが 、ソースコードの変更は行なっていません。 全体のハード 及びPIC側のソフトは 全体&PIC側を参照
 願います

       <プログラム>
  
        
        delegate void SetTextCallback(string text);//デリゲート宣言

      
        public Form1()
        {
            InitializeComponent();

            UpdateCOMPortList();    //使用可能COMポートリスト更新
        }


        private void UpdateCOMPortList()    //使用可能COMポートリスト作成
        {
            int i;
            bool foundDifference;

            i = 0;
            foundDifference = false;

            if (lstCOMPorts.Items.Count == SerialPort.GetPortNames().Length)
            {
                foreach (string s in SerialPort.GetPortNames())
                {
                   
                    if (lstCOMPorts.Items[i++].Equals(s) == false)
                    {
                        foundDifference = true;
                    }
                }
            }
            else
            {
                foundDifference = true;
            }

            if (foundDifference == false)
            {
                return;
            }

            lstCOMPorts.Items.Clear();
            foreach (string s in SerialPort.GetPortNames())
            {
                lstCOMPorts.Items.Add(s);
            }
            lstCOMPorts.SelectedIndex = 0;
        }

        private void timer1_Tick(object sender, System.EventArgs e) //COMポートリストの周期的更新
        {
                UpdateCOMPortList();
        }

        private void btnConnect_Click(object sender, System.EventArgs e)    //選択されたCOMポートをオープンする
        {
         
            try
            {
                serialPort1.PortName = lstCOMPorts.Items[lstCOMPorts.SelectedIndex].ToString();

                serialPort1.Open();  //COMポートオープン

                btnConnect.Enabled = false;
                lstCOMPorts.Enabled = false;
                btnClose.Enabled = true;

                txtDataReceived.Clear();
                txtDataReceived.AppendText("Connected.\r\n");
            }
            catch
            {
                //If there was an exception, then close the handle to 
                //  the device and assume that the device was removed
                btnClose_Click(this, null);
            }
        }

        private void btnClose_Click(object sender, System.EventArgs e)  //COMポートのクローズ
        {
            btnClose.Enabled = false;
            btnConnect.Enabled = true;
            lstCOMPorts.Enabled = true;

            try
            {
                serialPort1.DiscardInBuffer();  //入力バッファーのデータ破棄
                serialPort1.DiscardOutBuffer(); //出力バッファーのデータ破棄

                serialPort1.Close();
            }
            //If there was an exeception then there isn't much we can
            //  do.  The port is no longer available.
            catch { }
        }

        private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e) //データ受信
        {
            try
            {
                SetText(serialPort1.ReadExisting());
            }
            catch
            {
                //If there was an exception, then close the handle to 
                //  the device and assume that the device was removed
                btnClose_Click(this, null);
            }
        }

       
        private void SetText(string text)   //受信スレッドからメインスレッドへの書き出し
        {
            if (txtDataReceived.InvokeRequired)
            {
                SetTextCallback d = new SetTextCallback(SetText);
                Invoke(d, new object[] { text });
            }
            else
            {
                //If this function was called from the same thread that 
                //  holds the required objects then just add the text.
                txtDataReceived.AppendText(text);
            }
        }
        
         private void btnSendData_Click(object sender, System.EventArgs e)  //テキストボックス内のデータをCOMポート経由で送信
        {
            try
            {
                 serialPort1.Write(txtData.Text);
            }
            catch
            {
                //If there was an exception, then close the handle to 
                //  the device and assume that the device was removed
                btnClose_Click(this, null);
            }
        }
          

PIC18F4550−PC 間  CDCクラス通信
 ( 自作PICキバン + Microchip社デモソフト)

<動作結果>
    デモソフト:
        Basic Demo

@コンボボックスからCOM8ポートを選択

AConnectボタンをクリック
  
→リッチテキストボックスにConnectedが表示される

BPICキバン上のSWを押すと文字列
 Button Pressed--がUSB送信され、左記ウィンドウの リッチテキストボックスに表示される。

Cテキストボックスに5をキーインて、Send Dataボタンを クリックする。
  →PICキバン側で+1インクリメントされた6がUSB通信で返信され、これが左記リッチテキストボックスに表示される

      ★全体のハード 及びPIC側のソフト
                 → 全体&PIC側参照

    デモソフト:

        Serial Emulator


送信側(USB通信):PICDEMサンプルソフト
受信側(RS232C通信):Tera Term

     

@左記ウィンドウのテキストボックスに文字列
 Hellow World !!をキーインして、Send Dataボタンをクリックする

APIC側キバン(PICDEM相当自作キバン)では、USBケーブル経由送信されてきたたこの文字列を受信した後、この文字列をRS232Cケーブル経由でRS232C通信でエコーバックする

BPC側ではエコーバックされてきたデータを通信ソフトTera Termで受信し、これをウィンドウに表示する。
                      




      ★全体のハード 及びPIC側のソフト
                 → 全体&PIC側参照


(注)ウィンドウの背景にある画像は、本テーマとは関係ありません

    デモソフト:
        Serial Emulator


送信側(RS232C通信):Tera Term
受信側(USB通信):PICDEMサンプルソフト


@Tera Term(注)に Data from TeraTerm とキーイン後、エンターキーを押しRS232C経由でPIC側(PICDEM相当)にデータを送信する。
APIC側では受信データをそのままUSBケーブル経由でPC側に送信する。
BPC側ではPICDEMデモソフトの右記ウィンドウのリッチテキストボックスで受信データを表示する。


      ★全体のハード 及びPIC側のソフト
                 → 全体&PIC側参照

(注)TeraTermのローカルエコーがONであるのでキーインしたデータが表示されている。



■ PIC18 CDCクラス 通信 (液晶付き) 
  マイクロチップ社のMCHPFSUSB Framework 2.3をダウンロードすると入手できる公開ソフトをベースに設計した、PCとPIC18F4550間のUSB通信(CDCクラス 液晶付)の例を紹介します。 ハード 及びPIC側のソフトは こちらを参照願います 
            

<試作品仕様>
 ・PC側からデータをPIC側にUSB CDCクラス通信で送信する。
 ・PIC側では受信した文字列を液晶上段に、受信データに基づき返信したデータを液晶下段に表示する。
 ・PC側でも受信したデータをテキストボックスに表示する。
 ・PC側からの送信データ 及びPIC側からの返信データは以下とする。
     @ How are you ?     →  I am fine !!
     A Your name ?      →  My name is PIC
     B This is a pen      →  Pardon?                 

   <プログラム>


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO.Ports;      //要追加

namespace USB_LCD_Shift_Jis
{
    public partial class Form1 : Form
    {
        //文字を変換するクラスを宣言
        Encoding encSjis = Encoding.GetEncoding("shift-jis");
        Encoding encUni = Encoding.GetEncoding("unicode");

       delegate void SetTextCallback(string text);//デリゲート宣言
   
    

     
            




        public Form1()
        {
            InitializeComponent();
           
            UpdateCOMPortList();    //使用可能COMポートリストチェック

            SendData_ComboBox.Items.Clear();
            SendData_ComboBox.Items.Add("How are you ?   ");
            SendData_ComboBox.Items.Add("Your name ?     ");
            SendData_ComboBox.Items.Add("This is a pen   ");
            SendData_ComboBox.SelectedIndex = 0;        //コンボボックス選択値の初期値設定 

        }

        private void UpdateCOMPortList()    //使用可能COMポートチェック & リスト作成
        {
            int i;
            bool foundDifference;

            i = 0;
            foundDifference = false;

            if (Port_comboBox.Items.Count == SerialPort.GetPortNames().Length)
            {
                foreach (string s in SerialPort.GetPortNames())
                {

                    if (Port_comboBox.Items[i++].Equals(s) == false)
                    {
                        foundDifference = true;
                    }
                }
            }
            else
            {
                foundDifference = true;
            }

            if (foundDifference == false)
            {
                return;
            }

            Port_comboBox.Items.Clear();
            foreach (string s in SerialPort.GetPortNames())
            {
                Port_comboBox.Items.Add(s);
            }
            Port_comboBox.SelectedIndex = 0;
        }

  

        private void CloseBtn_Click(object sender, EventArgs e)
        {
            CloseBtn.Enabled = false;
            ConnectBtn.Enabled = true;
            Port_comboBox.Enabled = true;

            try
            {
                serialPort1.DiscardInBuffer();  //入力バッファーのデータ破棄
                serialPort1.DiscardOutBuffer(); //出力バッファーのデータ破棄

                serialPort1.Close();
            }
            //If there was an exeception then there isn't much we can
            //  do.  The port is no longer available.
            catch { }

        }

   
        private void timer1_Tick(object sender, EventArgs e)    //COMポートの周期的チェック&更新
        {
            UpdateCOMPortList();
        }

        private void ConnectBtn_Click_1(object sender, EventArgs e)
        {
            try
            {
                serialPort1.PortName = Port_comboBox.Items[Port_comboBox.SelectedIndex].ToString();

                serialPort1.Open();  //COMポートオープン

                ConnectBtn.Enabled = false;
                Port_comboBox.Enabled = false;
                CloseBtn.Enabled = true;

                Receive_textBox.Clear();
                Receive_textBox.AppendText("Connected.\r\n");
            }
            catch   // COMポートが開けない場合
            {
                CloseBtn_Click(this, null);
            }
        }

        private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)     //受信
        {
            try
            {
                byte[] byteRead = new byte[serialPort1.BytesToRead];  //受信文字

                serialPort1.Read(byteRead, 0, serialPort1.BytesToRead);//shift-JIS読み込み
                byte[] byteUni = Encoding.Convert(encSjis, encUni, byteRead);   //shift-JIS からunicodeに変換する
                string strUni = encUni.GetString(byteUni);  //配列を文字列に変換

                SetTextCallback SetTextCallback = new SetTextCallback(SetText);
                this.Invoke(SetTextCallback, strUni);


            }
            catch
            {
                //If there was an exception, then close the handle to 
                //  the device and assume that the device was removed
               CloseBtn_Click(this, null);
            }

        }

        private void SetText(string text)   //受信スレッドからメインスレッドへの書き出し
        {
            if (Receive_textBox.InvokeRequired)
            {
                SetTextCallback d = new SetTextCallback(SetText);
                Invoke(d, new object[] { text });
            }
            else
            {
                //If this function was called from the same thread that 
                //  holds the required objects then just add the text.
                Receive_textBox.AppendText(text);
            }
        }

        private void SendBtn_Click_1(object sender, EventArgs e)    //送信
        {
            try
            {
                string strSend = SendData_ComboBox.Text;
                if (!strSend.EndsWith("\r")) strSend += "\r";  //改行が無かったら \rを追加する
                serialPort1.Write(strSend);
            }
            catch
            {
                //If there was an exception, then close the handle to 
                //  the device and assume that the device was removed
                CloseBtn_Click(this, null);
            }

        }

    }

       
  
}

<動作結果>
 @ PC側のウィンドウから仮想COM8ポートを選択してを接続ボタンをクリックする。
 A USB接続が完了して受信データテキストボックスに Connected が表示される。
 B 送信データ用コンボボックススから How are you ? を選択して送信ボタンをクリックする。
 C PIC側で受信した受信データ How are you ? が液晶の上段に表示されています。
 D 返信データとして I am fine !! がプログラムで選択され、これを液晶下段に表示されています。また、同時にPC側にも送信されています。
 E この結果、PC側では 受信したデータ I am fine !! を受信データ用テキストボックスに表示しています。
 F 同様に Your name ?  を送信した場合 My name is PIC が返信された場合も表示されています。
 G 同様に This is a pen  を送信した場合 Pardon? が返信された場合も表示されています。
 

PC側 
送受信ウィンドウ
PIC側 液晶画面



■ PIC32MZ - PC間 CDCクラス ASCII文字列の送受信(キャラクタ液晶、Harmony)


PIC32MZ2048ECH144にキャラクタ液晶を接続した HarmonyによるCDCクラスのUSB通信 送受信の例を紹介します。   → PIC側  ソフト
   
  <試作品仕様>
    ・PCとPIC32MZ間で 文字列の送受信をおこなう。
    ・USBのライブラリはHarmonyのCDCクラスをつかうこと
    ・PC側から文字列に対して PIC側からそれぞれに対応した文字列を返信すること
     送受信の文字列は 以下とする
      PCからの送信文字列            PICからの返信文字列
      Japan\r               Tokyo    
      America\r              Washington
      U.K.\r                London
      France\r              Paris
    ・PICにはキャラくタ液晶を接続し 受信文字列と返信文字列を表示すること
    ・PIC側のダイアログのテキストボックスに受信文字列を表示のこと
    ・ 開発環境  PIC側: Harmony ver. 1.03   XC32 ver.1.34  MPLABX ver.2.35  PIC32MZ2048ECH144 revision 5
              PC側: VC# 2012


        プログラム 
   

   

          

   




using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; namespace Kanji_PC_PIC { public partial class Form1 : Form { //文字を変換するクラスを宣言 Encoding encSjis = Encoding.GetEncoding("shift-jis"); Encoding encUni = Encoding.GetEncoding("unicode"); public Form1() { InitializeComponent(); serialPort1.PortName = "COM3"; //COM1 ポート使用 serialPort1.BaudRate = 9600; //9600bps serialPort1.Parity = System.IO.Ports.Parity.None; //パリティなし serialPort1.DataBits = 8; //データ長 8ビット serialPort1.StopBits = System.IO.Ports.StopBits.One; //ストップビット 1 serialPort1.ReadTimeout = 500; //500mse 読み込みタイムアウト時間 serialPort1.WriteTimeout = 500; //500msec書き込みタイムアウト時間 comboBox1.Items.Clear(); comboBox1.Items.Add("Japan"); comboBox1.Items.Add("America"); comboBox1.Items.Add("U.K."); comboBox1.Items.Add("France"); // comboBox1.Items.Add("I_am_fine"); comboBox1.SelectedIndex = 0; } delegate void ReceiveDelegate(string str);//デリゲート宣言 private void SetText(string str) { richTextBox1.AppendText(str); } private void button1_Click(object sender, EventArgs e) { if(serialPort1.IsOpen == false)serialPort1.Open();//シリアルポートオープン string strSend = comboBox1.Text; if (!strSend.EndsWith("\r")) strSend += "\r"; //改行が無かったら \rを追加する Byte[] byteArry = encSjis.GetBytes(strSend); //'送信文字をShift-jisに変換してをByte配列に格納 serialPort1.Write(byteArry, 0, byteArry.Length); //送信 } private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e) { byte[] byteRead = new byte[serialPort1.BytesToRead]; //受信文字 serialPort1.Read(byteRead, 0, serialPort1.BytesToRead);//読み込み byte[] byteUni = Encoding.Convert(encSjis, encUni, byteRead); //shift-jis からunicodeに変換する string strUni = encUni.GetString(byteUni); //配列を文字列に変換 ReceiveDelegate receiveDelegate = new ReceiveDelegate(SetText); this.Invoke(receiveDelegate, strUni); } private void button2_Click(object sender, EventArgs e) { richTextBox1.Clear(); } private void button3_Click(object sender, EventArgs e) { serialPort1.Close(); } } }




 <動作結果>

   PIC側液晶画面
上段: 受信文字列
下段: 返信文字列
PC画面 
送信前     
 PCから "Japan\r"を
送信した場合
   
 PCから 以下の順番で
文字列を送信した場合
"Japan\r"
"America\r"
"U.K.\r"
"France\r"


   

PIC32MZ - PC間 (デバイスモード)HIDクラス 
    IO制御(LEDのOnOff、SW・VR電圧読込)
   (PIC32MZ評価ボード サンプルソフト)

                                     (サンプルプログラム(VC#プロジェクトファイル VC# 2017版) → ダウンロード
                                     (サンプルプログラム(実行ファイル) → ダウンロード

                                          ★ PIC側のハードとソフトは こちらを参照願います



<仕様>
  ・PCとPIC間のUSB通信はHIDクラスとする。
  ・PICはPIC32MZ2048EFH100を使用のこと。
  ・PC側のプログラムは、マイクロソフトのVC++で作成のこと。
  ・PC側はダイアログウィンドウを作成して、以下を実施できること。
               (PC側サンプルプログラム(実行ファイル) → ダウンロード)

     @ PIC側のLEDのON/OFF制御
     A PIC側のスイッチの状態のモニタ
     B PIC側の可変抵抗器の電圧もモニタ
  ・ダイアログウィンドウのボタンスイッチ(コントロール)のクリックにより、PIC側のLEDがON/OFFできること。
  ・ボタンスイッチの背景色はLEDがONの場合は赤色、LEDがOFFの場合は灰色とする。
  ・ボタンコントロールはLEDがONの場合は赤色に、OFFの場合は灰色とする。
  ・LEDがONの場合は画像を またOFFの場合は画像を表示のこと。
  ・PIC側のスイッチのON/OFF状態が分かるモニターとして 円形のオーバルシェープコントロールを使用のこと。
   尚、色はONの場合はオレンジ色、OFFの場合は灰色とする。
  ・スイッチがONの場合は画像を またOFFの場合は画像を表示のこと。画像は重ねて配置のこと。
  ・PIC側の可変抵抗器の電圧値に表示のこと。値は四捨五入して小数点以下2桁を表示のこと。
  ・入力電圧値がビジュアルにわかるようにプログレスバーでも表示のこと。
  ・途中切断も含め、USB通信がOFFの場合はLED,、スイッチ、可変抵抗器などに係る表示は行わないようにすること。
  ・PCのアプリケーションは100msec毎にPIC側の状態をサンプリングのこと。
  ・ PC側のダイアログウィンドウ例を以下に示す。      

    
   <USB通信OFFの場合>  <USB通信がONの場合>


    ・PIC側のモニタ用液晶として、I2C制御のキャラクタ液晶を用いること。
    ・PICが起動する時、キャラクタ液晶には以下を表示のこと。 
      1行目: USB device HID
      2行目: start !!
    ・USB通信が始まったら液晶に可変抵抗器の入力電圧をモニタする。
      1行目: AdValue= ◯◯◯◯   
      2行目: Volt= ◯◯◯◯[V]
     AdValueの〇〇〇〇は内臓ADコンバータの読み込み整数値、Voltは電圧換算値で小数点以下3桁を表示のこと。
    ・可変抵抗器の電圧のサンプリングタイムは50msecとする。
    ・USBケーブルが送受信中に抜かれたりして通信が切断された場合は LED、スイッチ、可変抵抗器のPictureBoxはDisableとなり ダイアログウィンドウ画面は 上記の <USB通信OFFの場合>の画面に戻ること。
   

以下、プログラム抜粋

Form1.cs [デザイン] 
Form1.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

using Microsoft.Win32.SafeHandles;
using System.Runtime.InteropServices;
using System.Threading;

namespace _050_dHID_LED_SW_VR
{
public partial class Form1 : Form
{
// Win32 API関数の呼び出し
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------BEGIN CUT AND PASTE BLOCK-----------------------------------------------------------------------------------

//Constant definitions from setupapi.h, which we aren't allowed to include directly since this is C#
internal const uint DIGCF_PRESENT = 0x02;
internal const uint DIGCF_DEVICEINTERFACE = 0x10;
//Constants for CreateFile() and other file I/O functions
internal const short FILE_ATTRIBUTE_NORMAL = 0x80;
internal const short INVALID_HANDLE_VALUE = -1;
internal const uint GENERIC_READ = 0x80000000;
internal const uint GENERIC_WRITE = 0x40000000;
internal const uint CREATE_NEW = 1;
internal const uint CREATE_ALWAYS = 2;
internal const uint OPEN_EXISTING = 3;
internal const uint FILE_SHARE_READ = 0x00000001;
internal const uint FILE_SHARE_WRITE = 0x00000002;
//Constant definitions for certain WM_DEVICECHANGE messages
internal const uint WM_DEVICECHANGE = 0x0219;
internal const uint DBT_DEVICEARRIVAL = 0x8000;
internal const uint DBT_DEVICEREMOVEPENDING = 0x8003;
internal const uint DBT_DEVICEREMOVECOMPLETE = 0x8004;
internal const uint DBT_CONFIGCHANGED = 0x0018;
//Other constant definitions
internal const uint DBT_DEVTYP_DEVICEINTERFACE = 0x05;
internal const uint DEVICE_NOTIFY_WINDOW_HANDLE = 0x00;
internal const uint ERROR_SUCCESS = 0x00;
internal const uint ERROR_NO_MORE_ITEMS = 0x00000103;
internal const uint SPDRP_HARDWAREID = 0x00000001;

//Various structure definitions for structures that this code will be using
internal struct SP_DEVICE_INTERFACE_DATA
{
internal uint cbSize; //DWORD
internal Guid InterfaceClassGuid; //GUID
internal uint Flags; //DWORD
internal uint Reserved; //ULONG_PTR MSDN says ULONG_PTR is "typedef unsigned __int3264 ULONG_PTR;"
}

internal struct SP_DEVICE_INTERFACE_DETAIL_DATA
{
internal uint cbSize; //DWORD
internal char[] DevicePath; //TCHAR array of any size
}

internal struct SP_DEVINFO_DATA
{
internal uint cbSize; //DWORD
internal Guid ClassGuid; //GUID
internal uint DevInst; //DWORD
internal uint Reserved; //ULONG_PTR MSDN says ULONG_PTR is "typedef unsigned __int3264 ULONG_PTR;"
}

internal struct DEV_BROADCAST_DEVICEINTERFACE
{
internal uint dbcc_size; //DWORD
internal uint dbcc_devicetype; //DWORD
internal uint dbcc_reserved; //DWORD
internal Guid dbcc_classguid; //GUID
internal char[] dbcc_name; //TCHAR array
}

//DLL Imports. Need these to access various C style unmanaged functions contained in their respective DLL files.
//--------------------------------------------------------------------------------------------------------------
//Returns a HDEVINFO type for a device information set. We will need the
//HDEVINFO as in input parameter for calling many of the other SetupDixxx() functions.
[DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern IntPtr SetupDiGetClassDevs(
ref Guid ClassGuid, //LPGUID Input: Need to supply the class GUID.
IntPtr Enumerator, //PCTSTR Input: Use NULL here, not important for our purposes
IntPtr hwndParent, //HWND Input: Use NULL here, not important for our purposes
uint Flags); //DWORD Input: Flags describing what kind of filtering to use.

//Gives us "PSP_DEVICE_INTERFACE_DATA" which contains the Interface specific GUID (different
//from class GUID). We need the interface GUID to get the device path.
[DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern bool SetupDiEnumDeviceInterfaces(
IntPtr DeviceInfoSet, //Input: Give it the HDEVINFO we got from SetupDiGetClassDevs()
IntPtr DeviceInfoData, //Input (optional)
ref Guid InterfaceClassGuid, //Input
uint MemberIndex, //Input: "Index" of the device you are interested in getting the path for.
ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData); //Output: This function fills in an "SP_DEVICE_INTERFACE_DATA" structure.

//SetupDiDestroyDeviceInfoList() frees up memory by destroying a DeviceInfoList
[DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern bool SetupDiDestroyDeviceInfoList(
IntPtr DeviceInfoSet); //Input: Give it a handle to a device info list to deallocate from RAM.

//SetupDiEnumDeviceInfo() fills in an "SP_DEVINFO_DATA" structure, which we need for SetupDiGetDeviceRegistryProperty()
[DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern bool SetupDiEnumDeviceInfo(
IntPtr DeviceInfoSet,
uint MemberIndex,
ref SP_DEVINFO_DATA DeviceInterfaceData);

//SetupDiGetDeviceRegistryProperty() gives us the hardware ID, which we use to check to see if it has matching VID/PID
[DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern bool SetupDiGetDeviceRegistryProperty(
IntPtr DeviceInfoSet,
ref SP_DEVINFO_DATA DeviceInfoData,
uint Property,
ref uint PropertyRegDataType,
IntPtr PropertyBuffer,
uint PropertyBufferSize,
ref uint RequiredSize);

//SetupDiGetDeviceInterfaceDetail() gives us a device path, which is needed before CreateFile() can be used.
[DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern bool SetupDiGetDeviceInterfaceDetail(
IntPtr DeviceInfoSet, //Input: Wants HDEVINFO which can be obtained from SetupDiGetClassDevs()
ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData, //Input: Pointer to an structure which defines the device interface.
IntPtr DeviceInterfaceDetailData, //Output: Pointer to a SP_DEVICE_INTERFACE_DETAIL_DATA structure, which will receive the device path.
uint DeviceInterfaceDetailDataSize, //Input: Number of bytes to retrieve.
ref uint RequiredSize, //Output (optional): The number of bytes needed to hold the entire struct
IntPtr DeviceInfoData); //Output (optional): Pointer to a SP_DEVINFO_DATA structure

//Overload for SetupDiGetDeviceInterfaceDetail(). Need this one since we can't pass NULL pointers directly in C#.
[DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern bool SetupDiGetDeviceInterfaceDetail(
IntPtr DeviceInfoSet, //Input: Wants HDEVINFO which can be obtained from SetupDiGetClassDevs()
ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData, //Input: Pointer to an structure which defines the device interface.
IntPtr DeviceInterfaceDetailData, //Output: Pointer to a SP_DEVICE_INTERFACE_DETAIL_DATA structure, which will contain the device path.
uint DeviceInterfaceDetailDataSize, //Input: Number of bytes to retrieve.
IntPtr RequiredSize, //Output (optional): Pointer to a DWORD to tell you the number of bytes needed to hold the entire struct
IntPtr DeviceInfoData); //Output (optional): Pointer to a SP_DEVINFO_DATA structure

//Need this function for receiving all of the WM_DEVICECHANGE messages. See MSDN documentation for
//description of what this function does/how to use it. Note: name is remapped "RegisterDeviceNotificationUM" to
//avoid possible build error conflicts.
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern IntPtr RegisterDeviceNotification(
IntPtr hRecipient,
IntPtr NotificationFilter,
uint Flags);

//Takes in a device path and opens a handle to the device.
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
static extern SafeFileHandle CreateFile(
string lpFileName,
uint dwDesiredAccess,
uint dwShareMode,
IntPtr lpSecurityAttributes,
uint dwCreationDisposition,
uint dwFlagsAndAttributes,
IntPtr hTemplateFile);

//Uses a handle (created with CreateFile()), and lets us write USB data to the device.
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
static extern bool WriteFile(
SafeFileHandle hFile,
byte[] lpBuffer,
uint nNumberOfBytesToWrite,
ref uint lpNumberOfBytesWritten,
IntPtr lpOverlapped);

//Uses a handle (created with CreateFile()), and lets us read USB data from the device.
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
static extern bool ReadFile(
SafeFileHandle hFile,
IntPtr lpBuffer,
uint nNumberOfBytesToRead,
ref uint lpNumberOfBytesRead,
IntPtr lpOverlapped);


//グローバル変数の定義
//--------------- Global Varibles Section ------------------
//USB related variables that need to have wide scope.
bool AttachedState = false; //Need to keep track of the USB device attachment status for proper plug and play operation.
bool AttachedButBroken = false;
SafeFileHandle WriteHandleToUSBDevice = null;
SafeFileHandle ReadHandleToUSBDevice = null;
String DevicePath = null; //Need the find the proper device path before you can open file handles.


//Variables used by the application/form updates.

bool LedBtnClicked = false; //LEDボタン クリック
bool StateChecked = false; //スイッチ、ADコンバータ入力電圧状態チェック
bool LedMode = false;
bool SwMode = false;

//Globally Unique Identifier (GUID) for HID class devices. Windows uses GUIDs to identify things.
Guid InterfaceClassGuid = new Guid(0x4d1e55b2, 0xf16f, 0x11cf, 0x88, 0xcb, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30);




//--------------- End of Global Varibles ------------------

//-------------------------------------------------------END CUT AND PASTE BLOCK-------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------

public Form1()
{
InitializeComponent();


groupBox1.Visible = false;
groupBox2.Visible = false;
groupBox3.Visible = false;

OnLed_pictureBox.Visible = false; //Led オン画像非表示
OffLed_pictureBox.Visible = true; //Led オフ画像表示

OnSw_pictureBox.Visible = false; //SW オン画像非表示
OffSw_pictureBox.Visible = true; //SW オフ画像表示

VR_progressBar.Value = 0;


//接続状態表示ランプの表示
pictureBox1.Image = new Bitmap(pictureBox1.Width, pictureBox1.Height); //pictureBox1のイメージデータのオブジェクト生成
Graphics g = Graphics.FromImage(pictureBox1.Image); //イメージからグラフィックスを作成
g.FillEllipse(Brushes.Gray, 10, 0, 50, 50);//灰色塗りつぶし 基点(10,0) 幅×高さ:50×50


}

private void USB_ConnectBtn_Click(object sender, EventArgs e)
{
try
{


//-------------------------------------------------------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------BEGIN CUT AND PASTE BLOCK-----------------------------------------------------------------------------------
//Additional constructor code

//Initialize tool tips, to provide pop up help when the mouse cursor is moved over objects on the form.

//ys
//ANxVoltageToolTip.SetToolTip(this.ANxVoltage_lbl, "If using a board/PIM without a potentiometer, apply an adjustable voltage to the I/O pin.");
//ANxVoltageToolTip.SetToolTip(this.progressBar1, "If using a board/PIM without a potentiometer, apply an adjustable voltage to the I/O pin.");
//ToggleLEDToolTip.SetToolTip(this.ToggleLEDs_btn, "Sends a packet of data to the USB device.");
//PushbuttonStateTooltip.SetToolTip(this.PushbuttonState_lbl, "Try pressing pushbuttons on the USB demo board/PIM.");
//ys

//Register for WM_DEVICECHANGE notifications. This code uses these messages to detect plug and play connection/disconnection events for USB devices
DEV_BROADCAST_DEVICEINTERFACE DeviceBroadcastHeader = new DEV_BROADCAST_DEVICEINTERFACE();
DeviceBroadcastHeader.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
DeviceBroadcastHeader.dbcc_size = (uint)Marshal.SizeOf(DeviceBroadcastHeader);
DeviceBroadcastHeader.dbcc_reserved = 0; //Reserved says not to use...
DeviceBroadcastHeader.dbcc_classguid = InterfaceClassGuid;

//Need to get the address of the DeviceBroadcastHeader to call RegisterDeviceNotification(), but
//can't use "&DeviceBroadcastHeader". Instead, using a roundabout means to get the address by
//making a duplicate copy using Marshal.StructureToPtr().
IntPtr pDeviceBroadcastHeader = IntPtr.Zero; //Make a pointer.
pDeviceBroadcastHeader = Marshal.AllocHGlobal(Marshal.SizeOf(DeviceBroadcastHeader)); //allocate memory for a new DEV_BROADCAST_DEVICEINTERFACE structure, and return the address
Marshal.StructureToPtr(DeviceBroadcastHeader, pDeviceBroadcastHeader, false); //Copies the DeviceBroadcastHeader structure into the memory already allocated at DeviceBroadcastHeaderWithPointer
RegisterDeviceNotification(this.Handle, pDeviceBroadcastHeader, DEVICE_NOTIFY_WINDOW_HANDLE);


//Now make an initial attempt to find the USB device, if it was already connected to the PC and enumerated prior to launching the application.
//If it is connected and present, we should open read and write handles to the device so we can communicate with it later.
//If it was not connected, we will have to wait until the user plugs the device in, and the WM_DEVICECHANGE callback function can process
//the message and again search for the device.
if (CheckIfPresentAndGetUSBDevicePath()) //Check and make sure at least one device with matching VID/PID is attached
{
uint ErrorStatusWrite;
uint ErrorStatusRead;


//We now have the proper device path, and we can finally open read and write handles to the device.
WriteHandleToUSBDevice = CreateFile(DevicePath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
ErrorStatusWrite = (uint)Marshal.GetLastWin32Error();
ReadHandleToUSBDevice = CreateFile(DevicePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
ErrorStatusRead = (uint)Marshal.GetLastWin32Error();

if ((ErrorStatusWrite == ERROR_SUCCESS) && (ErrorStatusRead == ERROR_SUCCESS))
{
AttachedState = true; //Let the rest of the PC application know the USB device is connected, and it is safe to read/write to it
AttachedButBroken = false;
//StatusBox_txtbx.Text = "my1_Device Found, AttachedState = TRUE";
}
else //for some reason the device was physically plugged in, but one or both of the read/write handles didn't open successfully...
{
AttachedState = false; //Let the rest of this application known not to read/write to the device.
AttachedButBroken = true; //Flag so that next time a WM_DEVICECHANGE message occurs, can retry to re-open read/write pipes
if (ErrorStatusWrite == ERROR_SUCCESS)
WriteHandleToUSBDevice.Close();
if (ErrorStatusRead == ERROR_SUCCESS)
ReadHandleToUSBDevice.Close();
}
}
else //Device must not be connected (or not programmed with correct firmware)
{
AttachedState = false;
AttachedButBroken = false;
}

if (AttachedState == true)
{
ConnectLabel.Text = "接続完了";
//接続状態表示ランプの表示色 → 橙色

groupBox1.Visible = true;
groupBox2.Visible = true;
groupBox3.Visible = true;

OnLed_pictureBox.Visible = false; //Led オン画像非表示
OffLed_pictureBox.Visible = true; //Led オフ画像表示

OnSw_pictureBox.Visible = false; //SW オン画像非表示
OffSw_pictureBox.Visible = true; //SW オフ画像表示

pictureBox1.Image = new Bitmap(pictureBox1.Width, pictureBox1.Height); //pictureBox1のイメージデータのオブジェクト生成
Graphics g = Graphics.FromImage(pictureBox1.Image); //イメージからグラフィックスを作成
g.FillEllipse(Brushes.Orange, 10, 0, 50, 50);//橙色塗りつぶし 基点(10,0) 幅×高さ:50×50

timer1.Enabled = true;

LedBtnClicked = false; //LEDボタン クリック
StateChecked = false; //スイッチ状態チェック
LedMode = false;
SwMode = false;

}
else
{
//StatusBox_txtbx.Text = "Device not found, verify connect/correct firmware";
}

ReadWriteThread.RunWorkerAsync(); //Recommend performing USB read/write operations in a separate thread. Otherwise,
//the Read/Write operations are effectively blocking functions and can lock up the
//user interface if the I/O operations take a long time to complete.

//-------------------------------------------------------END CUT AND PASTE BLOCK-------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------


}
catch
{

}

}



//-------------------------------------------------------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------BEGIN CUT AND PASTE BLOCK-----------------------------------------------------------------------------------

//FUNCTION: CheckIfPresentAndGetUSBDevicePath()
//PURPOSE: Check if a USB device is currently plugged in with a matching VID and PID
//INPUT: Uses globally declared String DevicePath, globally declared GUID, and the MY_DEVICE_ID constant.
//OUTPUT: Returns BOOL. TRUE when device with matching VID/PID found. FALSE if device with VID/PID could not be found.
// When returns TRUE, the globally accessable "DetailedInterfaceDataStructure" will contain the device path
// to the USB device with the matching VID/PID.

bool CheckIfPresentAndGetUSBDevicePath()
{
/*
Before we can "connect" our application to our USB embedded device, we must first find the device.
A USB bus can have many devices simultaneously connected, so somehow we have to find our device only.
This is done with the Vendor ID (VID) and Product ID (PID). Each USB product line should have
a unique combination of VID and PID.

Microsoft has created a number of functions which are useful for finding plug and play devices. Documentation
for each function used can be found in the MSDN library. We will be using the following functions (unmanaged C functions):

SetupDiGetClassDevs() //provided by setupapi.dll, which comes with Windows
SetupDiEnumDeviceInterfaces() //provided by setupapi.dll, which comes with Windows
GetLastError() //provided by kernel32.dll, which comes with Windows
SetupDiDestroyDeviceInfoList() //provided by setupapi.dll, which comes with Windows
SetupDiGetDeviceInterfaceDetail() //provided by setupapi.dll, which comes with Windows
SetupDiGetDeviceRegistryProperty() //provided by setupapi.dll, which comes with Windows
CreateFile() //provided by kernel32.dll, which comes with Windows

In order to call these unmanaged functions, the Marshal class is very useful.

We will also be using the following unusual data types and structures. Documentation can also be found in
the MSDN library:

PSP_DEVICE_INTERFACE_DATA
PSP_DEVICE_INTERFACE_DETAIL_DATA
SP_DEVINFO_DATA
HDEVINFO
HANDLE
GUID

The ultimate objective of the following code is to get the device path, which will be used elsewhere for getting
read and write handles to the USB device. Once the read/write handles are opened, only then can this
PC application begin reading/writing to the USB device using the WriteFile() and ReadFile() functions.

Getting the device path is a multi-step round about process, which requires calling several of the
SetupDixxx() functions provided by setupapi.dll.
*/

try
{
IntPtr DeviceInfoTable = IntPtr.Zero;
SP_DEVICE_INTERFACE_DATA InterfaceDataStructure = new SP_DEVICE_INTERFACE_DATA();
SP_DEVICE_INTERFACE_DETAIL_DATA DetailedInterfaceDataStructure = new SP_DEVICE_INTERFACE_DETAIL_DATA();
SP_DEVINFO_DATA DevInfoData = new SP_DEVINFO_DATA();

uint InterfaceIndex = 0;
uint dwRegType = 0;
uint dwRegSize = 0;
uint dwRegSize2 = 0;
uint StructureSize = 0;
IntPtr PropertyValueBuffer = IntPtr.Zero;
bool MatchFound = false;
uint ErrorStatus;
uint LoopCounter = 0;

//Use the formatting: "Vid_xxxx&Pid_xxxx" where xxxx is a 16-bit hexadecimal number.
//Make sure the value appearing in the parathesis matches the USB device descriptor
//of the device that this aplication is intending to find.
String DeviceIDToFind = "Vid_04d8&Pid_003f";

//First populate a list of plugged in devices (by specifying "DIGCF_PRESENT"), which are of the specified class GUID.
DeviceInfoTable = SetupDiGetClassDevs(ref InterfaceClassGuid, IntPtr.Zero, IntPtr.Zero, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);

if (DeviceInfoTable != IntPtr.Zero)
{
//Now look through the list we just populated. We are trying to see if any of them match our device.
while (true)
{
InterfaceDataStructure.cbSize = (uint)Marshal.SizeOf(InterfaceDataStructure);
if (SetupDiEnumDeviceInterfaces(DeviceInfoTable, IntPtr.Zero, ref InterfaceClassGuid, InterfaceIndex, ref InterfaceDataStructure))
{
ErrorStatus = (uint)Marshal.GetLastWin32Error();
if (ErrorStatus == ERROR_NO_MORE_ITEMS) //Did we reach the end of the list of matching devices in the DeviceInfoTable?
{ //Cound not find the device. Must not have been attached.
SetupDiDestroyDeviceInfoList(DeviceInfoTable); //Clean up the old structure we no longer need.
return false;
}
}
else //Else some other kind of unknown error ocurred...
{
ErrorStatus = (uint)Marshal.GetLastWin32Error();
SetupDiDestroyDeviceInfoList(DeviceInfoTable); //Clean up the old structure we no longer need.
return false;
}

//Now retrieve the hardware ID from the registry. The hardware ID contains the VID and PID, which we will then
//check to see if it is the correct device or not.

//Initialize an appropriate SP_DEVINFO_DATA structure. We need this structure for SetupDiGetDeviceRegistryProperty().
DevInfoData.cbSize = (uint)Marshal.SizeOf(DevInfoData);
SetupDiEnumDeviceInfo(DeviceInfoTable, InterfaceIndex, ref DevInfoData);

//First query for the size of the hardware ID, so we can know how big a buffer to allocate for the data.
SetupDiGetDeviceRegistryProperty(DeviceInfoTable, ref DevInfoData, SPDRP_HARDWAREID, ref dwRegType, IntPtr.Zero, 0, ref dwRegSize);

//Allocate a buffer for the hardware ID.
//Should normally work, but could throw exception "OutOfMemoryException" if not enough resources available.
PropertyValueBuffer = Marshal.AllocHGlobal((int)dwRegSize);

//Retrieve the hardware IDs for the current device we are looking at. PropertyValueBuffer gets filled with a
//REG_MULTI_SZ (array of null terminated strings). To find a device, we only care about the very first string in the
//buffer, which will be the "device ID". The device ID is a string which contains the VID and PID, in the example
//format "Vid_04d8&Pid_003f".
SetupDiGetDeviceRegistryProperty(DeviceInfoTable, ref DevInfoData, SPDRP_HARDWAREID, ref dwRegType, PropertyValueBuffer, dwRegSize, ref dwRegSize2);

//Now check if the first string in the hardware ID matches the device ID of the USB device we are trying to find.
String DeviceIDFromRegistry = Marshal.PtrToStringUni(PropertyValueBuffer); //Make a new string, fill it with the contents from the PropertyValueBuffer

Marshal.FreeHGlobal(PropertyValueBuffer); //No longer need the PropertyValueBuffer, free the memory to prevent potential memory leaks

//Convert both strings to lower case. This makes the code more robust/portable accross OS Versions
DeviceIDFromRegistry = DeviceIDFromRegistry.ToLowerInvariant();
DeviceIDToFind = DeviceIDToFind.ToLowerInvariant();
//Now check if the hardware ID we are looking at contains the correct VID/PID
MatchFound = DeviceIDFromRegistry.Contains(DeviceIDToFind);
if (MatchFound == true)
{
//Device must have been found. In order to open I/O file handle(s), we will need the actual device path first.
//We can get the path by calling SetupDiGetDeviceInterfaceDetail(), however, we have to call this function twice: The first
//time to get the size of the required structure/buffer to hold the detailed interface data, then a second time to actually
//get the structure (after we have allocated enough memory for the structure.)
DetailedInterfaceDataStructure.cbSize = (uint)Marshal.SizeOf(DetailedInterfaceDataStructure);
//First call populates "StructureSize" with the correct value
SetupDiGetDeviceInterfaceDetail(DeviceInfoTable, ref InterfaceDataStructure, IntPtr.Zero, 0, ref StructureSize, IntPtr.Zero);
//Need to call SetupDiGetDeviceInterfaceDetail() again, this time specifying a pointer to a SP_DEVICE_INTERFACE_DETAIL_DATA buffer with the correct size of RAM allocated.
//First need to allocate the unmanaged buffer and get a pointer to it.
IntPtr pUnmanagedDetailedInterfaceDataStructure = IntPtr.Zero; //Declare a pointer.
pUnmanagedDetailedInterfaceDataStructure = Marshal.AllocHGlobal((int)StructureSize); //Reserve some unmanaged memory for the structure.
DetailedInterfaceDataStructure.cbSize = 6; //Initialize the cbSize parameter (4 bytes for DWORD + 2 bytes for unicode null terminator)
Marshal.StructureToPtr(DetailedInterfaceDataStructure, pUnmanagedDetailedInterfaceDataStructure, false); //Copy managed structure contents into the unmanaged memory buffer.

//Now call SetupDiGetDeviceInterfaceDetail() a second time to receive the device path in the structure at pUnmanagedDetailedInterfaceDataStructure.
if (SetupDiGetDeviceInterfaceDetail(DeviceInfoTable, ref InterfaceDataStructure, pUnmanagedDetailedInterfaceDataStructure, StructureSize, IntPtr.Zero, IntPtr.Zero))
{
//Need to extract the path information from the unmanaged "structure". The path starts at (pUnmanagedDetailedInterfaceDataStructure + sizeof(DWORD)).
IntPtr pToDevicePath = new IntPtr((uint)pUnmanagedDetailedInterfaceDataStructure.ToInt32() + 4); //Add 4 to the pointer (to get the pointer to point to the path, instead of the DWORD cbSize parameter)
DevicePath = Marshal.PtrToStringUni(pToDevicePath); //Now copy the path information into the globally defined DevicePath String.

//We now have the proper device path, and we can finally use the path to open I/O handle(s) to the device.
SetupDiDestroyDeviceInfoList(DeviceInfoTable); //Clean up the old structure we no longer need.
Marshal.FreeHGlobal(pUnmanagedDetailedInterfaceDataStructure); //No longer need this unmanaged SP_DEVICE_INTERFACE_DETAIL_DATA buffer. We already extracted the path information.
return true; //Returning the device path in the global DevicePath String
}
else //Some unknown failure occurred
{
uint ErrorCode = (uint)Marshal.GetLastWin32Error();
SetupDiDestroyDeviceInfoList(DeviceInfoTable); //Clean up the old structure.
Marshal.FreeHGlobal(pUnmanagedDetailedInterfaceDataStructure); //No longer need this unmanaged SP_DEVICE_INTERFACE_DETAIL_DATA buffer. We already extracted the path information.
return false;
}
}

InterfaceIndex++;
//Keep looping until we either find a device with matching VID and PID, or until we run out of devices to check.
//However, just in case some unexpected error occurs, keep track of the number of loops executed.
//If the number of loops exceeds a very large number, exit anyway, to prevent inadvertent infinite looping.
LoopCounter++;
if (LoopCounter == 10000000) //Surely there aren't more than 10 million devices attached to any forseeable PC...
{
return false;
}
}//end of while(true)
}
return false;
}//end of try
catch
{
//Something went wrong if PC gets here. Maybe a Marshal.AllocHGlobal() failed due to insufficient resources or something.
return false;
}
}
//-------------------------------------------------------END CUT AND PASTE BLOCK-------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------




//-------------------------------------------------------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------BEGIN CUT AND PASTE BLOCK-----------------------------------------------------------------------------------
//This is a callback function that gets called when a Windows message is received by the form.
//We will receive various different types of messages, but the ones we really want to use are the WM_DEVICECHANGE messages.
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_DEVICECHANGE)
{
if (((int)m.WParam == DBT_DEVICEARRIVAL) || ((int)m.WParam == DBT_DEVICEREMOVEPENDING) || ((int)m.WParam == DBT_DEVICEREMOVECOMPLETE) || ((int)m.WParam == DBT_CONFIGCHANGED))
{
//WM_DEVICECHANGE messages by themselves are quite generic, and can be caused by a number of different
//sources, not just your USB hardware device. Therefore, must check to find out if any changes relavant
//to your device (with known VID/PID) took place before doing any kind of opening or closing of handles/endpoints.
//(the message could have been totally unrelated to your application/USB device)

if (CheckIfPresentAndGetUSBDevicePath()) //Check and make sure at least one device with matching VID/PID is attached
{
//If executes to here, this means the device is currently attached and was found.
//This code needs to decide however what to do, based on whether or not the device was previously known to be
//attached or not.
if ((AttachedState == false) || (AttachedButBroken == true)) //Check the previous attachment state
{
uint ErrorStatusWrite;
uint ErrorStatusRead;

//We obtained the proper device path (from CheckIfPresentAndGetUSBDevicePath() function call), and it
//is now possible to open read and write handles to the device.
WriteHandleToUSBDevice = CreateFile(DevicePath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
ErrorStatusWrite = (uint)Marshal.GetLastWin32Error();
ReadHandleToUSBDevice = CreateFile(DevicePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
ErrorStatusRead = (uint)Marshal.GetLastWin32Error();

if ((ErrorStatusWrite == ERROR_SUCCESS) && (ErrorStatusRead == ERROR_SUCCESS))
{
AttachedState = true; //Let the rest of the PC application know the USB device is connected, and it is safe to read/write to it
AttachedButBroken = false;

}
else //for some reason the device was physically plugged in, but one or both of the read/write handles didn't open successfully...
{
AttachedState = false; //Let the rest of this application known not to read/write to the device.
AttachedButBroken = true; //Flag so that next time a WM_DEVICECHANGE message occurs, can retry to re-open read/write pipes
if (ErrorStatusWrite == ERROR_SUCCESS)
WriteHandleToUSBDevice.Close();
if (ErrorStatusRead == ERROR_SUCCESS)
ReadHandleToUSBDevice.Close();
}
}
//else we did find the device, but AttachedState was already true. In this case, don't do anything to the read/write handles,
//since the WM_DEVICECHANGE message presumably wasn't caused by our USB device.
}
else //Device must not be connected (or not programmed with correct firmware)
{
if (AttachedState == true) //If it is currently set to true, that means the device was just now disconnected
{
AttachedState = false;
WriteHandleToUSBDevice.Close();
ReadHandleToUSBDevice.Close();
}
AttachedState = false;
AttachedButBroken = false;
}
}
} //end of: if(m.Msg == WM_DEVICECHANGE)


if((m.Msg == WM_DEVICECHANGE) && ((int)m.WParam == DBT_DEVICEREMOVECOMPLETE))
{

ConnectLabel.Text = "未接続";

groupBox1.Visible = false;
groupBox2.Visible = false;
groupBox3.Visible = false;

OnLed_pictureBox.Visible = false; //Led オン画像非表示
OffLed_pictureBox.Visible = false; //Led オフ画像表示

OnSw_pictureBox.Visible = false; //SW オン画像非表示
OffSw_pictureBox.Visible = false; //SW オフ画像表示

pictureBox1.Image = new Bitmap(pictureBox1.Width, pictureBox1.Height); //pictureBox1のイメージデータのオブジェクト生成
Graphics g = Graphics.FromImage(pictureBox1.Image); //イメージからグラフィックスを作成
g.FillEllipse(Brushes.Gray, 10, 0, 50, 50);//灰色塗りつぶし 基点(10,0) 幅×高さ:50×50

timer1.Enabled = false;

ReadWriteThread.CancelAsync();
}
base.WndProc(ref m);
} //end of: WndProc() function

//-------------------------------------------------------END CUT AND PASTE BLOCK-------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------



private void ReadWriteThread_DoWork(object sender, DoWorkEventArgs e)
{

//-------------------------------------------------------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------BEGIN CUT AND PASTE BLOCK-----------------------------------------------------------------------------------

/*This thread does the actual USB read/write operations (but only when AttachedState == true) to the USB device.
It is generally preferrable to write applications so that read and write operations are handled in a separate
thread from the main form. This makes it so that the main form can remain responsive, even if the I/O operations
take a very long time to complete.

Since this is a separate thread, this code below executes independently from the rest of the
code in this application. All this thread does is read and write to the USB device. It does not update
the form directly with the new information it obtains (such as the ANxx/POT Voltage or pushbutton state).
The information that this thread obtains is stored in atomic global variables.
Form updates are handled by the FormUpdateTimer Tick event handler function.

This application sends packets to the endpoint buffer on the USB device by using the "WriteFile()" function.
This application receives packets from the endpoint buffer on the USB device by using the "ReadFile()" function.
Both of these functions are documented in the MSDN library. Calling ReadFile() is a not perfectly straight
foward in C# environment, since one of the input parameters is a pointer to a buffer that gets filled by ReadFile().
The ReadFile() function is therefore called through a wrapper function ReadFileManagedBuffer().

All ReadFile() and WriteFile() operations in this example project are synchronous. They are blocking function
calls and only return when they are complete, or if they fail because of some event, such as the user unplugging
the device. It is possible to call these functions with "overlapped" structures, and use them as non-blocking
asynchronous I/O function calls.

Note: This code may perform differently on some machines when the USB device is plugged into the host through a
USB 2.0 hub, as opposed to a direct connection to a root port on the PC. In some cases the data rate may be slower
when the device is connected through a USB 2.0 hub. This performance difference is believed to be caused by
the issue described in Microsoft knowledge base article 940021:
http://support.microsoft.com/kb/940021/en-us

Higher effective bandwidth (up to the maximum offered by interrupt endpoints), both when connected
directly and through a USB 2.0 hub, can generally be achieved by queuing up multiple pending read and/or
write requests simultaneously. This can be done when using asynchronous I/O operations (calling ReadFile() and
WriteFile() with overlapped structures). The Microchip HID USB Bootloader application uses asynchronous I/O
for some USB operations and the source code can be used as an example.*/


Byte[] OUTBuffer = new byte[65]; //Allocate a memory buffer equal to the OUT endpoint size + 1
Byte[] INBuffer = new byte[65]; //Allocate a memory buffer equal to the IN endpoint size + 1
uint BytesWritten = 0;
uint BytesRead = 0;

bool LedMode2;
uint ADCValue;

while (true)
{
try
{
if (AttachedState == true) //Do not try to use the read/write handles unless the USB device is attached and ready
{
//// 接続確認確認
//OUTBuffer[0] = 0; // Report ID
//OUTBuffer[1] = 0x30; // 接続確認

//// 送信
//WriteFile(WriteHandleToUSBDevice, OUTBuffer, 65, ref BytesWritten, IntPtr.Zero);
////受信
//ReadFileManagedBuffer(ReadHandleToUSBDevice, INBuffer, 65, ref BytesRead, IntPtr.Zero);

//if (!((INBuffer[3] == 'R') && //'R' 'e' 'a' 'd' 'y' 文字を受信できない場合
// (INBuffer[4] == 'e') &&
// (INBuffer[5] == 'a') &&
// (INBuffer[6] == 'd') &&
// (INBuffer[7] == 'y')))

//{
// NoArgDelegate nrd = new NoArgDelegate(ConnectAlarm); //デリゲートのインスタンス生成、初期化
// this.Invoke(nrd); //デリゲート関数呼び出し //this.Invoke(デリゲートのインスタンス, 第1引数);


//}





//LED 点灯/消灯
if (LedBtnClicked == true)
{

OUTBuffer[0] = 0; // Report ID
OUTBuffer[1] = 0x80; // LED制御コマンド
OUTBuffer[2] = 0x30; // LEDアドレス

if (LedMode == false) //消灯していたなら
{
LedMode = true;
OUTBuffer[3] = 0x31; // Led点灯 //0x31 = '1'
}
else
{
LedMode = false;
OUTBuffer[3] = 0x30; // Led消灯 //0x30 = '0'
}

// 送信
WriteFile(WriteHandleToUSBDevice, OUTBuffer, 65, ref BytesWritten, IntPtr.Zero);
//受信
ReadFileManagedBuffer(ReadHandleToUSBDevice, INBuffer, 65, ref BytesRead, IntPtr.Zero);



if(INBuffer[3] == 0x31) //点灯
{
LedMode2 = true;
}
else
{
LedMode2 = false;
}

LedBtnClicked = false;


LedStatusDelegate lsd = new LedStatusDelegate(LedMonitor); //デリゲートのインスタンス生成、初期化
this.Invoke(lsd, LedMode2); //デリゲート関数呼び出し //this.Invoke(デリゲートのインスタンス, 第1引数);
}


//スイッチの状態読み出し
if (StateChecked == true)
{
//スイッチの状態読込
OUTBuffer[0] = 0; // Report ID
OUTBuffer[1] = 0x81; // SW状態要求コマンド
OUTBuffer[2] = 0x31; // SW1

// 送信
WriteFile(WriteHandleToUSBDevice, OUTBuffer, 65, ref BytesWritten, IntPtr.Zero);
//受信
ReadFileManagedBuffer(ReadHandleToUSBDevice, INBuffer, 65, ref BytesRead, IntPtr.Zero);


if (INBuffer[3] == 0x30) SwMode = true; //SW: ON
else SwMode = false; //SW:OFF

SwStatusDelegate ssd = new SwStatusDelegate(SwMonitor); //デリゲートのインスタンス生成、初期化
this.Invoke(ssd, SwMode); //デリゲート関数呼び出し //this.Invoke(デリゲートのインスタンス, 第1引数);



//可変抵抗器出力電圧のAD変換値 読込
OUTBuffer[0] = 0; // Report ID
OUTBuffer[1] = 0x82; // 可変抵抗器outのAD変換値読込
OUTBuffer[2] = 0x31; // VR1

// 送信
WriteFile(WriteHandleToUSBDevice, OUTBuffer, 65, ref BytesWritten, IntPtr.Zero);
//受信
ReadFileManagedBuffer(ReadHandleToUSBDevice, INBuffer, 65, ref BytesRead, IntPtr.Zero);


ADCValue = (uint)(INBuffer[4] * 256) + INBuffer[3];

ADCStatusDelegate lsd = new ADCStatusDelegate(ADCMonitor); //デリゲートのインスタンス生成、初期化
this.Invoke(lsd, ADCValue); //デリゲート関数呼び出し //this.Invoke(デリゲートのインスタンス, 第1引数);

StateChecked = false;
}
}

}
catch
{
//Exceptions can occur during the read or write operations. For example,
//exceptions may occur if for instance the USB device is physically unplugged
//from the host while the above read/write functions are executing.

//Don't need to do anything special in this case. The application will automatically
//re-establish communications based on the global AttachedState boolean variable used
//in conjunction with the WM_DEVICECHANGE messages to dyanmically respond to Plug and Play
//USB connection events.
}

} //end of while(true) loop
//-------------------------------------------------------END CUT AND PASTE BLOCK-------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------

}




//-------------------------------------------------------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------BEGIN CUT AND PASTE BLOCK-----------------------------------------------------------------------------------

//--------------------------------------------------------------------------------------------------------------------------
//FUNCTION: ReadFileManagedBuffer()
//PURPOSE: Wrapper function to call ReadFile()
//
//INPUT: Uses managed versions of the same input parameters as ReadFile() uses.
//
//OUTPUT: Returns boolean indicating if the function call was successful or not.
// Also returns data in the byte[] INBuffer, and the number of bytes read.
//
//Notes: Wrapper function used to call the ReadFile() function. ReadFile() takes a pointer to an unmanaged buffer and deposits
// the bytes read into the buffer. However, can't pass a pointer to a managed buffer directly to ReadFile().
// This ReadFileManagedBuffer() is a wrapper function to make it so application code can call ReadFile() easier
// by specifying a managed buffer.
//--------------------------------------------------------------------------------------------------------------------------
public unsafe bool ReadFileManagedBuffer(SafeFileHandle hFile, byte[] INBuffer, uint nNumberOfBytesToRead, ref uint lpNumberOfBytesRead, IntPtr lpOverlapped)
{
IntPtr pINBuffer = IntPtr.Zero;

try
{
pINBuffer = Marshal.AllocHGlobal((int)nNumberOfBytesToRead); //Allocate some unmanged RAM for the receive data buffer.

if (ReadFile(hFile, pINBuffer, nNumberOfBytesToRead, ref lpNumberOfBytesRead, lpOverlapped))
{
Marshal.Copy(pINBuffer, INBuffer, 0, (int)lpNumberOfBytesRead); //Copy over the data from unmanged memory into the managed byte[] INBuffer
Marshal.FreeHGlobal(pINBuffer);
return true;
}
else
{
Marshal.FreeHGlobal(pINBuffer);
return false;
}

}
catch
{
if (pINBuffer != IntPtr.Zero)
{
Marshal.FreeHGlobal(pINBuffer);
}
return false;
}
}



//-------------------------------------------------------------------------------------------------------



private void LedBtn_Click(object sender, EventArgs e)
{
LedBtnClicked = true;
}

//デリゲートの型を定義
delegate void LedStatusDelegate(bool arg1);
private void LedMonitor(bool LedMode) //LEDの状態をモニタ //デリゲート用の関数を定義
{
if (LedMode == true) //LED点灯の場合
{
OnLed_pictureBox.Visible = true; //ランプ点灯のピクチャボックス →表示
OffLed_pictureBox.Visible = false; //ランプ消灯のピクチャボックス →非表示
LedBtn.BackColor = Color.Red; //LED ON/OFFボタンの背景色 →赤色

}
else //LED消灯の場合
{
OnLed_pictureBox.Visible = false;
OffLed_pictureBox.Visible = true;
LedBtn.BackColor = Color.Gray;

}
}

private void timer1_Tick(object sender, EventArgs e)
{
StateChecked = true;
}

//デリゲートの型を定義
delegate void SwStatusDelegate(bool arg1);

private void SwMonitor(bool SwMode)
{
if(SwMode == true)
{
OnSw_pictureBox.Visible = true;
OffSw_pictureBox.Visible = false;

pictureBox2.Image = new Bitmap(pictureBox2.Width, pictureBox2.Height); //pictureBox1のイメージデータのオブジェクト生成
Graphics g = Graphics.FromImage(pictureBox2.Image); //イメージからグラフィックスを作成
g.FillEllipse(Brushes.Orange, 10, 0, 50, 50);//橙色塗りつぶし 基点(10,0) 幅×高さ:50×50
}
else
{
OnSw_pictureBox.Visible = false;
OffSw_pictureBox.Visible = true;

pictureBox2.Image = new Bitmap(pictureBox2.Width, pictureBox2.Height); //pictureBox1のイメージデータのオブジェクト生成
Graphics g = Graphics.FromImage(pictureBox2.Image); //イメージからグラフィックスを作成
g.FillEllipse(Brushes.Gray, 10, 0, 50, 50);//灰色塗りつぶし 基点(10,0) 幅×高さ:50×50
}
}


//デリゲートの型を定義
delegate void ADCStatusDelegate(uint arg1);
private void ADCMonitor(uint adcValue) //LEDの状態をモニタ //デリゲート用の関数を定義
{
int temp;
double Volt;

Volt = (adcValue * 3.3) / 4096;
VR_textBox.Text = string.Format("{0:f2} [V]", Volt); //小数点以下2桁にして表示

//四捨五入
temp = (int)((((float)adcValue * 100) / 4096) + 0.5); //0.5を足して、小数点以下を切り捨てる

VR_progressBar.Value = temp;
}



//デリゲートの型を定義
delegate void NoArgDelegate();
private void ConnectAlarm() //LEDの状態をモニタ //デリゲート用の関数を定義
{
ConnectLabel.Text = "未接続";

groupBox1.Visible = false;
groupBox2.Visible = false;
groupBox3.Visible = false;

OnLed_pictureBox.Visible = false; //Led オン画像非表示
OffLed_pictureBox.Visible = false; //Led オフ画像表示

OnSw_pictureBox.Visible = false; //SW オン画像非表示
OffSw_pictureBox.Visible = false; //SW オフ画像表示

pictureBox1.Image = new Bitmap(pictureBox1.Width, pictureBox1.Height); //pictureBox1のイメージデータのオブジェクト生成
Graphics g = Graphics.FromImage(pictureBox1.Image); //イメージからグラフィックスを作成
g.FillEllipse(Brushes.Gray, 10, 0, 50, 50);//灰色塗りつぶし 基点(10,0) 幅×高さ:50×50

timer1.Enabled = false;

}
}
}

<動作結果>(→ 動画:1080pのHD動画を見ることができます。)

 

 モード PC画面 & PIC32MZ評価ボード 備考(部分拡大、詳細)
@ PCの後、PICを起動した状態の写真です。
 USBは未接続の状態です。
A 接続ボタンをクリックしてUSB通信が確立した状態です。
 スイッチのON/OFF状態や可変抵抗器の電圧はリアルタイムモニタリングされています。
   
B 接続ボタンをクリックして LEDのON/OFFボタンをクリックすることによって、PIC側のLEDを点灯させた状態です。 ON/OFFボタンスイッチが灰色→赤色に変化して、ランプの画像がOFFの画像からONの画像に変わっています。
 尚、スイッチはOFF、可変抵抗器の電圧は0vであることをモニタリングしています。
 
C PIC側のスイッチを押した状態です。シェープの色がオレンジ色になって、スイッチがONの画像が表示されています。  
D PIC側の可変抵抗器を回して、入力電圧が約1.2vの状態の写真です。 入力電圧のテキストボックスには1.21 [V]が表示され、またプログレスバーは、 36.7 (= 1.21/3.3) %を示しています。
  
 
E PICの電源をOFFにした状態です。 USB通信が遮断されたことを検出して PCのダイアログウィンドウはUSB通信が確立していない状態になっています。  



PIC32MZ - PC間 (デバイスモード)CDCクラス 
     文字列送受信            (PIC32MZ評価ボード サンプルソフト)

                      (サンプルプログラム(VC#プロジェクトファイル VC# 2017版) → ダウンロード
                      (サンプルプログラム(実行ファイル) → ダウンロード

★ PIC側のハードとソフトは、以下を参照願います

グラフィック液晶の
  ラベルウィジェットへの表示    
  プログラム名称       URL   
 Harmony
Ver2.04
Harmony
Ver.3
 有 ラベルにPCからのUSB受信文字列を表示
 URL URL
 無  USB(デバイスモード)CDCクラスPIC−PC間
文字列送受信
 URL URL




<仕様>
 ・PCとPIC32MZ間でUSB接続による文字列の送受信をおこなう。
 ・USBのライブラリはHarmonyのCDCクラスをつかうこと
 ・PC側から文字列に対して PIC側からそれぞれに対応した文字列を返信すること
  送受信の文字列は 以下とする

      PCからの送信文字列 PICからの返信文字列
Japan\r Tokyo 
America\r Washington
U.K.\r  London
France\r  Paris
How are you?\ Pardon?

 ・PICのキャラくタ液晶はI2Cインターフェースのキャラクタ液晶とし、以下を表示のこと。
   1行目 …… PCからの受信文字列
   2行目 …… PCへの送信文字列

 ・PC側のアプリケーションは マイクロソフトのVC#で作成のこと。
 ・PCのアプリケーションプログラムの基本動作は、以下とする。
   尚、USBコネクタの脱着を検出し USB接続状態表示(接続完了/未接続 及びランプ表示の灰色/オレンジ色)に反映のこと。

操作/動作 @プログラム立ち上がり後の初期状態
  USB未接続状態、 
A接続ボタンをPC使用可能COMポートを検索し、使用COMポートのコンボボックスにセットする。セレクトされたCOMポートがPIC側とUSB接続できない場合は COMポート変更等を促すメッセージを表示する。 Bコンボボックスから別のCOMポートを選択する。
PC画面
操作/動作 CPIC側と接続できたCOMポートを選択できた場合は準備完了"I am Ready"のメッセージを表示する。 またこれと同時に送信ボタン、送信データ用のコンボボックス、"接続完了"の文字 及びオレンジ色のランプ表示も行う。 D送信データをコンボボックスから選択後、送信ボタンをクリックしてPIC側に送信する。PIC側からの返信データを受信データ用のリッチテキストボックスに表示する。
 尚、表示された受信データはクリアボタンで消去できること。
E停止ボタンをクリックするとUSB接続は遮断されること。 これに伴い使用COMポートは空欄となり、"未接続"の文字が表示されランプ表示は灰色となること。
PC画面




以下、プログラム抜粋

 Form1.cs [デザイン]
 
 Form1.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO.Ports; //追加 シリアルポートコンポーネント用


namespace USB_CDC_Str_SendReceive2
{
public partial class Form1 : Form
{
int OK_COM = 0; //COMポート接続完了フラグ
bool LampCOM = false; //COM/USB 接続表示
bool PerformClick = false; //偽クリック発生 //ピクチャーボックス再描画
bool RichTextBoxWritable = false; //リッチテキストボックス書き込み禁止(許可)



//文字を変換するクラスを宣言
Encoding encSjis = Encoding.GetEncoding("shift-jis");
Encoding encUni = Encoding.GetEncoding("unicode");

delegate void ReceiveDelegate(string str);//デリゲート宣言
public Form1()
{
InitializeComponent();

SendDataCBox.Items.Clear();
SendDataCBox.Items.Add("Japan");
SendDataCBox.Items.Add("America");
SendDataCBox.Items.Add("U.K.");
SendDataCBox.Items.Add("France");
SendDataCBox.Items.Add("How are you ?");
SendDataCBox.SelectedIndex = 0;

SendDataBtn.Visible = false;
SendDataCBox.Visible = false;
SendDataLabel.Visible = false;


}

enum WINDOW_MESSAGES : uint
{
WM_DEVICECHANGE = 0x0219,
}

private enum DBT
{
DBT_DEVICEARRIVAL = 0x8000,
DBT_DEVICEQUERYREMOVE = 0x8001,
DBT_DEVICEQUERYREMOVEFAILED = 0x8002,
DBT_DEVICEREMOVEPENDING = 0x8003,
DBT_DEVICEREMOVECOMPLETE = 0x8004,
}

protected override void WndProc(ref Message m)
{

switch ((WINDOW_MESSAGES)m.Msg)
{
case WINDOW_MESSAGES.WM_DEVICECHANGE:
// pnpEvent(this, EventArgs.Empty);

switch ((DBT)m.WParam.ToInt32())
{
case DBT.DBT_DEVICEARRIVAL:
//ドライブが装着された時の処理を書く
LampCOM = true;
ConnectLabel.Text = "接続完了";
PerformClick = true; //クリックによるリッチテキストクリア動作を無効化
ClearBtn.PerformClick(); //イベントを発生させ、Form1_Paint()を実行して描画を行う。
PerformClick = false; //クリックによるリッチテキストクリア動作を有効化
RichTextBoxWritable = true; //リッチテキストボックス書き込み許可
break;
case DBT.DBT_DEVICEREMOVECOMPLETE:
//ドライブが取り外されたされた時の処理を書く
LampCOM = false;
ConnectLabel.Text = "未接続";
RichTextBoxWritable = false; //リッチテキストボックス書き込み禁止
break;
}



break;
}
base.WndProc(ref m);
}


private void Form1_Paint(object sender, PaintEventArgs e) //USB接続/未接続のイメージランプ描画
{
if(LampCOM == false)
{
Graphics g = pictureBox1.CreateGraphics(); //CreateGraphicsメソッドによりGraphicsオブジェクトを取得
g.FillEllipse(Brushes.Gray, 10, 0, 50, 50); //灰色塗りつぶし 基点(10,0) 幅×高さ:50×50
}
else
{
Graphics g = pictureBox1.CreateGraphics(); //CreateGraphicsメソッドによりGraphicsオブジェクトを取得
g.FillEllipse(Brushes.Orange, 10, 0, 50, 50);//橙色塗りつぶし 基点(10,0) 幅×高さ:50×50
}



}

private void SetText(string str)
{
if(RichTextBoxWritable == true) //リッチテキストボックス書き込み許可の場合
{
richTextBox1.AppendText(str); //文字列追加
richTextBox1.AppendText("\n"); //改行

}
}

private void ComPortConnectTry() //COMポート接続チェック
{



try
{
serialPort1.DiscardInBuffer(); //受信バッファのデータクリア

string strSend = "Are you Ready ?";
if (!strSend.EndsWith("\r")) strSend += "\r"; //改行が無かったら \rを追加する
Byte[] byteArry = encSjis.GetBytes(strSend); //'送信文字をShift-jisに変換してをByte配列に格納
serialPort1.Write(byteArry, 0, byteArry.Length); //送信

string OK_message = "I am Ready\r\n";

string Response = serialPort1.ReadLine(); //返信データ抜き取り
OK_COM = OK_message.CompareTo(Response); //返信データが "I am Ready\r\n"の場合 1 を返す
if (OK_COM == 1)
{
ConnectLabel.Text = "接続完了";
SendDataBtn.Visible = true;
SendDataCBox.Visible = true;
SendDataLabel.Visible = true;

LampCOM = true;
ConnectBtn.Enabled = true;

}
MessageBox.Show(Response);
RichTextBoxWritable = true;

}
catch (Exception ex2)
{
OK_COM = 0;
ConnectLabel.Text = "未接続";
SendDataBtn.Visible = false;
SendDataCBox.Visible = false;
SendDataLabel.Visible = false;

LampCOM = false;
ConnectBtn.Enabled = false;
RichTextBoxWritable = false; //リッチテキストボックス書き込み禁止


if (ex2 is TimeoutException)
{
string TimeoutStr = "1. COMポートをかえてみてください \n2. COMポートコネクタの抜けなど断線がないチェックしてください。";
MessageBox.Show(ex2.Message + "\n" + TimeoutStr);
}
else MessageBox.Show(ex2.Message);
}


}




private void ConnectBtn_Click(object sender, EventArgs e) //接続ボタン
{
if(ConnectBtn.Text == "停止")
{
serialPort1.Close();//シリアルポートオープン
ConnectBtn.Text = "接続";
SendDataBtn.Visible = false;
SendDataCBox.Visible = false;
SendDataLabel.Visible = false;
ConnectLabel.Text = "未接続";
LampCOM = false;
ComCBox.Items.Clear();
ComCBox.Text = "";

return;
}


if (ConnectBtn.Enabled == true) ConnectBtn.Text = "停止";
else ConnectBtn.Text = "接続";



string[] PortList = SerialPort.GetPortNames();

try
{
ComCBox.Items.Clear();

//シリアルポート名をコンボボックスにセット
foreach (string PortName in PortList)
{
ComCBox.Items.Add(PortName);
}
if (ComCBox.Items.Count > 0)
{
ComCBox.SelectedIndex = 0;
}

serialPort1.PortName = ComCBox.SelectedItem.ToString();

// serialPort1.PortName = "COM3"; //COM1 ポート使用
// serialPort1.PortName = "COM4"; //COM1 ポート使用
serialPort1.BaudRate = 9600; //9600bps
serialPort1.Parity = System.IO.Ports.Parity.None; //パリティなし
serialPort1.DataBits = 8; //データ長 8ビット
serialPort1.StopBits = System.IO.Ports.StopBits.One; //ストップビット 1
serialPort1.ReadTimeout = 500; //500mse 読み込みタイムアウト時間
serialPort1.WriteTimeout = 500; //500msec書き込みタイムアウト時間

serialPort1.Open();//シリアルポートオープン

}
catch (Exception ex)
{
DialogResult result = MessageBox.Show
(ex.Message + "\n" + "COMポートがひらけません");
}

ComPortConnectTry(); //COMポート接続チェック

}

private void SendDataBtn_Click(object sender, EventArgs e) //データ送信ボタン
{
try
{
if (serialPort1.IsOpen == false) serialPort1.Open();//シリアルポートオープン

serialPort1.DiscardInBuffer(); //受信バッファのデータクリア

string strSend = SendDataCBox.Text;
if (!strSend.EndsWith("\r")) strSend += "\r"; //改行が無かったら \rを追加する
Byte[] byteArry = encSjis.GetBytes(strSend); //'送信文字をShift-jisに変換してをByte配列に格納
serialPort1.Write(byteArry, 0, byteArry.Length); //送信
}
catch (Exception ex)
{
DialogResult result = MessageBox.Show
(ex.Message + "\n" + "COMポートがひらけません");
}
}

private void ClearBtn_Click(object sender, EventArgs e) //クリアボタン
{
if(PerformClick == false)richTextBox1.Clear(); //マウスでの実際のクリックが発生した場合のみ リッチテキストクリア
}

private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
{

byte[] byteRead = new byte[serialPort1.BytesToRead]; //受信文字

serialPort1.Read(byteRead, 0, serialPort1.BytesToRead);//読み込み
byte[] byteUni = Encoding.Convert(encSjis, encUni, byteRead); //shift-jis からunicodeに変換する
string strUni = encUni.GetString(byteUni); //配列を文字列に変換
ReceiveDelegate receiveDelegate = new ReceiveDelegate(SetText);
this.Invoke(receiveDelegate, strUni);
}

private void ComCBox_TextChanged(object sender, EventArgs e)
{

if (serialPort1.IsOpen == true)
{
serialPort1.Close();//シリアルポートクローズ

serialPort1.PortName = ComCBox.SelectedItem.ToString();
serialPort1.BaudRate = 9600; //9600bps
serialPort1.Parity = System.IO.Ports.Parity.None; //パリティなし
serialPort1.DataBits = 8; //データ長 8ビット
serialPort1.StopBits = System.IO.Ports.StopBits.One; //ストップビット 1
serialPort1.ReadTimeout = 500; //500mse 読み込みタイムアウト時間
serialPort1.WriteTimeout = 500; //500msec書き込みタイムアウト時間

serialPort1.Open();//シリアルポートオープン

ComPortConnectTry(); //COMポート接続チェック
}
}

}
}



<動作結果>(→ 動画:1080pのHD動画を見ることができます。)

操作/動作 PC画面 & PIC32MZ評価ボード 備考
@プログラム立ち上がり後の初期状態
  USB未接続状態
A接続ボタンをPC使用可能COMポートを検索し、使用COMポートのコンボボックスにセットする。セレクトされたCOMポートがPIC側とUSB接続できない場合は COMポート変更等を促すメッセージを表示する。
Bコンボボックスから別のCOMポートを選択する。
CPIC側と接続できたCOMポートを選択できた場合は準備完了"I am Ready"のメッセージを表示する。 またこれと同時に送信ボタン、送信データ用のコンボボックス、"接続完了"の文字 及びオレンジ色のランプ表示も行う。
D送信データをコンボボックスから選択後、送信ボタンをクリックしてPIC側に送信する。PIC側からの返信データを受信データ用のリッチテキストボックスに表示する。
 尚、表示された受信データはクリアボタンで消去できること。
E停止ボタンをクリックするとUSB接続は遮断されること。 これに伴い使用COMポートは空欄となり、"未接続"の文字が表示されランプ表示は灰色となること。

 USBメモリ 抜き差し検出

 すべてのWindowsメッセージがWndProcメソッドでイベントに置き換えられるわけではありません。所要のイベントを検出するには
WndProcメソッドをオーバーライドしてWindowsメッセージで判別します。
  実験の結果 DBT_DEVICEARRIVAL / DBT_DEVICEREMOVECOMPLETEでは USBメモリとマイクロチップのUSB(デバイスモード)
CDCクラスのUSBコネクタ脱着が検出できました。 しかし USB接続のキーボードやマウスの USBコネクタ脱着は検出できませんでした。
USBコネクタの脱着イベントを正確に取得したい場合はWindowsメッセージに係る資料を精読する必要があります。
実行結果
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WinPro_ReDraw_PictBox
{
public partial class Form1 : Form
{
bool LED = false;
public Form1()
{
InitializeComponent();
}

enum WINDOW_MESSAGES : uint
{
WM_DEVICECHANGE = 0x0219,
}

private enum DBT
{
DBT_DEVICEARRIVAL = 0x8000,
DBT_DEVICEQUERYREMOVE = 0x8001,
DBT_DEVICEQUERYREMOVEFAILED = 0x8002,
DBT_DEVICEREMOVEPENDING = 0x8003,
DBT_DEVICEREMOVECOMPLETE = 0x8004,
}

protected override void WndProc(ref Message m) //WndProcメソッドのオーバーライド
{
switch ((WINDOW_MESSAGES)m.Msg) //WINDOW_MESSAGESの判別
{
case WINDOW_MESSAGES.WM_DEVICECHANGE:

switch ((DBT)m.WParam.ToInt32())
{
case DBT.DBT_DEVICEARRIVAL: //ドライブが装着された時の処理を書く
LED = true;
button1.PerformClick(); //button1をクリックしなくとも クリックイベントを発生
// → PictureBox1の再描画イベントを発生
break;
case DBT.DBT_DEVICEREMOVECOMPLETE: //ドライブが取り外されたされた時の処理を書く
LED = false;
button1.PerformClick(); //button1をクリックしなくとも クリックイベントを発生
// → PictureBox1の再描画イベントを発生
break;
default:
break;
}
break;
default:
break;
}
base.WndProc(ref m);
}

private void Form1_Paint(object sender, PaintEventArgs e)
{
Graphics g = pictureBox1.CreateGraphics(); //CreateGraphicsメソッドによりGraphicsオブジェクトを取得

if (LED == false)
{
g.FillEllipse(Brushes.Gray, 10, 0, 50, 50); //灰色塗りつぶし 基点(10,0) 幅×高さ:50×50
}
else
{
g.FillEllipse(Brushes.Orange, 10, 0, 50, 50);//橙色塗りつぶし 基点(10,0) 幅×高さ:50×50
}
}
}
}
 

<USBメモリ抜き出し後>
及び<起動後>




<USBメモリ挿入後>